#region Using

using System;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Globalization;

#endregion

namespace SemanticEngine.Apml
{
	public  static class ApmlReader
	{

		public static ApmlDocument Read(Uri url)
		{
			XmlDocument doc = new XmlDocument();
			doc.Load(url.ToString());
			return Read(doc);
		}

		public static ApmlDocument Read(Stream stream)
		{
			XmlDocument doc = new XmlDocument();
			doc.Load(stream);
			return Read(doc);
		}

		public static ApmlDocument Read(XmlDocument document)
		{
			ApmlDocument apml = new ApmlDocument();

			ParseHead(document, apml);
			ParseBody(document, apml);


			return apml;
		}

		private static void ParseHead(XmlDocument document, ApmlDocument apml)
		{
			XmlNode head = document.SelectSingleNode("APML/Head");
			if (head != null)
			{
				if (head.SelectSingleNode("Title") != null)
					apml.Title = head.SelectSingleNode("Title").InnerText;

				if (head.SelectSingleNode("Generator") != null)
					apml.Generator = head.SelectSingleNode("Generator").InnerText;

				if (head.SelectSingleNode("UserEmail") != null)
					apml.UserEmail = head.SelectSingleNode("UserEmail").InnerText;

				DateTime dateCreated;
				if (head.SelectSingleNode("DateCreated") != null && DateTime.TryParse(head.SelectSingleNode("DateCreated").InnerText, out dateCreated))
				{
					apml.DateCreated = dateCreated;
				}
			}
		}

		private static void ParseBody(XmlDocument document, ApmlDocument apml)
		{
			XmlNode body = document.SelectSingleNode("APML/Body");
			if (body != null)
			{
				// TODO: implemnt defaultProfile
				//string defaultProfile = body.Attributes["defaultprofile"] != null ? body.Attributes["defaultprofile"].InnerText : null;
				XmlNodeList profiles = body.SelectNodes("//Profile");
				foreach (XmlNode child in profiles)
				{
					ParseProfile(child, apml);
				}
			}
		}

		private static void ParseProfile(XmlNode node, ApmlDocument apml)
		{
			ApmlProfile profile = new ApmlProfile();
			profile.Name = node.Attributes["name"] != null ? node.Attributes["name"].InnerText : null;

			ParseConcepts(node, profile);
			ParseSources(node, profile);

			apml.Profiles.Add(profile);
		}

		private static void ParseConcepts(XmlNode node, ApmlProfile profile)
		{
			XmlNodeList concepts = node.SelectNodes(".//Concept");
			if (concepts.Count == 0)
				return;

			string parentName = concepts[0].ParentNode.ParentNode.Name;
			foreach (XmlNode c in concepts)
			{
				Concept concept = new Concept();

				if (c.Attributes["key"] != null)
					concept.Name = c.Attributes["key"].InnerText;

				concept.Rank = ParseRank(c);
				concept.DataType = parentName.Equals("ExplicitData", StringComparison.OrdinalIgnoreCase) ? DataType.Explicit : DataType.Implicit;

				if (c.Attributes["from"] != null)
					concept.From = c.Attributes["from"].InnerText;

				DateTime dateCreated;
				if (c.Attributes["updated"] != null && DateTime.TryParse(c.Attributes["updated"].InnerText, out dateCreated))
					concept.DateUpdated = dateCreated;

				profile.Concepts.Add(concept);
			}

			profile.Concepts.Sort(delegate(Concept c1, Concept c2)
			{
				return c2.Rank.CompareTo(c1.Rank);
			});
		}

		private static void ParseSources(XmlNode node, ApmlProfile profile)
		{
			XmlNodeList sources = node.SelectNodes(".//Source");
			if (sources.Count == 0)
				return;

			string parentName = sources[0].ParentNode.ParentNode.Name;
			foreach (XmlNode c in sources)
			{
				Source source = new Source();

				Uri url;
				if (c.Attributes["key"] != null && Uri.TryCreate(c.Attributes["key"].InnerText, UriKind.Absolute, out url))
					source.Url = url;

				if (c.Attributes["name"] != null)
					source.Name = c.Attributes["name"].InnerText;

				source.Rank = ParseRank(c);
				source.DataType = parentName.Equals("ExplicitData", StringComparison.OrdinalIgnoreCase) ? DataType.Explicit : DataType.Implicit;

				if (c.Attributes["from"] != null)
					source.From = c.Attributes["from"].InnerText;

				if (c.Attributes["type"] != null)
					source.ContentType = c.Attributes["type"].InnerText;

				DateTime dateCreated;
				if (c.Attributes["updated"] != null && DateTime.TryParse(c.Attributes["updated"].InnerText, out dateCreated))
					source.DateUpdated = dateCreated;

				ParseAuthor(c, source);

				profile.Sources.Add(source);
			}

			profile.Sources.Sort(delegate(Source s1, Source s2)
			{
				return s2.Rank.CompareTo(s1.Rank);
			});
		}

		private static void ParseAuthor(XmlNode c, Source source)
		{
			XmlNode authorNode = c.SelectSingleNode("Author");
			if (authorNode != null)
			{
				Author author = new Author();

				if (authorNode.Attributes["key"] != null)
					author.Name = authorNode.Attributes["key"].InnerText;

				if (authorNode.Attributes["from"] != null)
					author.From = authorNode.Attributes["from"].InnerText;

				author.Rank = ParseRank(authorNode);

				DateTime dateCreated;
				if (c.Attributes["updated"] != null && DateTime.TryParse(c.Attributes["updated"].InnerText, out dateCreated))
					source.DateUpdated = dateCreated;

				source.Author = author;
			}
		}

		private static double ParseRank(XmlNode node)
		{	
			if (node.Attributes["value"] != null)
			{
				double rank = 0;
				string value = node.Attributes["value"].InnerText.Replace(",", ".");
				if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out rank))
				{
					return rank;
				}
			}

			return 0;
		}

	}
}
